#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function
import os
import sys
import time
import subprocess
import signal
import select
from .log import logger
from .process import kill_process_tree
import traceback


class CommandResult(object):
    def __init__(
            self,
            command="",
            stdout="",
            stderr="", tee="", exit_status=None, duration=0):
        self.command = command
        self.stdout = stdout
        self.stderr = stderr
        self.exit_status = exit_status
        self.duration = duration
        self.tee = tee

    def __str__(self):
        return ("Command: %s\n"
                "Exit status: %s\n"
                "Duration: %s\n"
                "STDOUT: %s\n"
                "STDERR: %s\n"
                % (self.command, self.exit_status, self.duration,
                    self.stdout, self.stderr)
                )


class ExecCmd(object):
    """
    run shell cmd:
        from process import BjJob
        task = BjJob('ls')
        result = task.run()
        print(result.stdout)
        print(result.stderr)
    """

    def __init__(
            self, command, timeout=None,
            env=None, logpath=None, save2mem=True, is_quiet=True,
            muted=False):
        self.command = command
        self.result = CommandResult(command)
        self.env = env
        self.timeout = timeout
        self.save2mem = save2mem
        self.log_4_stdout_fd = None
        self.log_4_stderr_fd = None
        self.is_quiet = is_quiet
        self.muted = muted
        if self.timeout:
            self.stoptime = time.time() + timeout
        else:
            self.stoptime = sys.maxsize
        if timeout is not None and timeout <= 0:
            logger.error("Timeout reached not to spawn a task")
            self.sp = None
        else:
            self.sp = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                shell=True,
                preexec_fn=os.setpgid(0, 0),
                env=self.env)
            if logpath:
                self.log_4_stdout_fd = open(
                    os.path.join(logpath, "stdout.log"), "a")
                self.log_4_stderr_fd = open(
                    os.path.join(logpath, "stderr.log"), "a")
                if not self.save2mem:
                    data = "-" * 80 + "\n"
                    data += "{}\n".format(time.strftime('%Y-%m-%d %H:%M:%S'))
                    data += "{}\n".format(command)
                    data += "-" * 80 + "\n"
                    self.save_log(data)

    def save_log(self, data, mode=True):
        if self.save2mem:
            if mode is True:
                self.result.stdout = self.result.stdout + data
            else:
                self.result.stderr = self.result.stderr + data
                self.result.stdout = self.result.stdout + data
        if not self.log_4_stdout_fd:
            return
        if mode is True:
            try:
                self.log_4_stdout_fd.write(data)
            except UnicodeEncodeError:
                self.log_4_stdout_fd.write(data.encode("utf8"))
        else:
            try:
                self.log_4_stdout_fd.write(data)
                self.log_4_stderr_fd.write(data)
            except UnicodeEncodeError:
                self.log_4_stdout_fd.write(data.encode("utf8"))
                self.log_4_stderr_fd.write(data.encode("utf8"))

    def __enter__(self):
        return self

    def __exit__(self, _exc_type, _exc_value, _traceback):
        if self.log_4_stdout_fd:
            self.log_4_stdout_fd.close()
        if self.log_4_stderr_fd:
            self.log_4_stderr_fd.close()

    def get_pid(self):
        return self.sp.pid

    def ocupized(self):
        op = {
            0: '-',
            1: '\\',
            2: '|',
            3: '/',
        }
        i = 0
        while True:
            i = i % 4
            yield op[i]
            i = i + 1

    def save_log_by_type(self, piptype):
        data = self.read_output(piptype)
        self.save_log(data, piptype)
        if data.strip() != "":
            for i in data.split('\n'):
                logger.debug(i)

    def terminate_all(self):
        if self.result.exit_status is None:
            if self.sp.poll() is None:
                signal_queue = [signal.SIGTERM, signal.SIGKILL]
                for sig in signal_queue:
                    kill_process_tree(self.sp.pid, sig)
                    if self.sp.poll() is not None:
                        self.result.exit_status = self.sp.poll()
                        break

    def run(self):
        if self.sp is None:
            return self.result
        start = time.time()

        if self.timeout:
            time_left = self.stoptime - time.time()
        else:
            time_left = None
        status = None
        while not self.timeout or time_left > 0:
            readlist = [self.sp.stdout, self.sp.stderr]
            read_ready, _, _ = select.select(readlist, [], [], 1)
            if self.sp.stdout in read_ready:
                self.save_log_by_type(True)
            else:
                pass
            if self.sp.stderr in read_ready:
                self.save_log_by_type(False)

            status = self.sp.poll()
            if status is not None:
                self.result.exit_status = status
                break
            time_left = self.stoptime - time.time()

        self.terminate_all()

        for i in (True, False):
            self.save_log_by_type(i)

        self.result.duration = time.time() - start
        self.sp.stdout.close()
        self.sp.stderr.close()

        logger.debug(self.result)

        return self.result

    def read_output(self, isstdout):
        if isstdout:
            pipe = self.sp.stdout
        else:
            pipe = self.sp.stderr
        data = ""
        while select.select([pipe], [], [], 0)[0]:
            bufferline = pipe.readline()
            try:
                bufferline = bufferline.decode('utf-8')
            except Exception:
                bufferline = str(bufferline)
            if bufferline == "":
                break
            data += bufferline
        if not self.is_quiet:
            try:
                print(data,end='')
            except Exception as e:
                logger.debug("-------------------------------")
                raise BaseException("{}\n{}".format(e, traceback.format_exc()))
        return data
